/**
 * Copyright 2012 Roger Cockshutt
 * krakenbrau@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.parallelrl.process;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;
import java.util.Vector;

import com.googlecode.parallelrl.ParallelRL;
import com.googlecode.parallelrl.data.ecs.ECSManager;

/**
 * @author Roger Cockshutt
 *
 */
public abstract class EventSystem {
	
	private static TreeMap<Long, List<Long>> eventSchedule = new TreeMap<>();
	private static List<Long> allEvents = new Vector<>();
	
	public static Long getNextStep() {
		if (eventSchedule.isEmpty()) {
			return Game.currentStep + 1;
		}
		return eventSchedule.firstKey();
	}
	
	public static Long newEvent(HashMap<String, String> event) {
		Long time = Long.valueOf(event.get("time"));
		Long entity = Long.valueOf(event.get("entity"));
		String action = event.get("action");
		String paramString = event.get("parameters");
		String condObject = event.get("conditionObject");
		String condComparator = event.get("conditionComparator");
		String condValue = event.get("conditionValue");
		Long newEvent = newEvent(time, entity, action, paramString);
		if (condObject != null && condComparator != null && condValue != null) {
			setCondition(newEvent, condObject, condComparator, condValue);
		}
		return newEvent;
	}
	
	public static Long newEvent(Long id, Long time, Long entity, String action, String... parameters) {
		ECSManager.createEntity(id);
		ECSManager.setComponent(id, "Time", time.toString());
		ECSManager.setComponent(id, "Entity", entity.toString());
		ECSManager.setComponent(id, "Action", action);
		String paramString = parameters[0];
		for (int i = 1; i < parameters.length; i++) {
			paramString += "," + parameters[i];
		}
		ECSManager.setComponent(id, "Parameters", paramString);
		if (eventSchedule.get(time) == null) {
			eventSchedule.put(time, new Vector<Long>());
		}
		eventSchedule.get(time).add(id);
		allEvents.add(id);
		return id;
	}
	
	public static Long newEvent(Long time, Long entity, String action, String... parameters) {
		Long event = ECSManager.createEntity();
		ECSManager.setComponent(event, "Time", time.toString());
		ECSManager.setComponent(event, "Entity", entity.toString());
		ECSManager.setComponent(event, "Action", action);
		String paramString = parameters[0];
		for (int i = 1; i < parameters.length; i++) {
			paramString += "," + parameters[i];
		}
		ECSManager.setComponent(event, "Parameters", paramString);
		if (eventSchedule.get(time) == null) {
			eventSchedule.put(time, new Vector<Long>());
		}
		eventSchedule.get(time).add(event);
		allEvents.add(event);
		return event;
	}
	
	public static void setCondition(Long event, String object, String comparator, String value) {
		if (!allEvents.contains(event)) {
			return;
		}
		ECSManager.setComponent(event, "ConditionObject", object);
		ECSManager.setComponent(event, "ConditionComparator", comparator);
		ECSManager.setComponent(event, "ConditionValue", value);
	}
	
	private static Boolean checkCondition(String object, String comparator, String value) {
		String[] splitObject = object.split(",");
		if (splitObject.length < 2) {
			return false;
		}
		String objectName = splitObject[0];
		Long objectEntity = Long.valueOf(splitObject[1]);
		if (!ECSManager.hasComponent(objectEntity, objectName)) {
			return false;
		}
		String objectValue = ECSManager.getComponent(objectEntity, objectName).value();
		switch (comparator) {
		case "=":
			return Double.valueOf(objectValue) == Double.valueOf(value);
		case "<":
			return Double.valueOf(objectValue) < Double.valueOf(value);
		case ">":
			return Double.valueOf(objectValue) > Double.valueOf(value);
		case "<=":
			return Double.valueOf(objectValue) <= Double.valueOf(value);
		case ">=":
			return Double.valueOf(objectValue) >= Double.valueOf(value);
		case "!=":
			return Double.valueOf(objectValue) != Double.valueOf(value);
		case "~":
			return objectValue.equals(value);
		case "!~":
			return !objectValue.equals(value);
		case "~=":
			return objectValue.contains(value);
		case "!~=":
			return !objectValue.contains(value);
		default:
			return false;
		}
	}
	
	public static void resolveCurrentEvents() {
		List<Long> currentEvents = new Vector<>();
		for (Long eventID : eventSchedule.get(Game.currentStep)) {
			currentEvents.add(eventID);
		}
		eventSchedule.remove(Game.currentStep);
		for (Long eventID : currentEvents) {
			resolveEvent(eventID);
			allEvents.remove(eventID);
			ECSManager.killEntity(eventID);
		}
		currentEvents.clear();
	}
	
	public static void clear() {
		eventSchedule.clear();
		allEvents.clear();
	}
	
	public static Boolean isEvent(Long entityID) {
		if (allEvents.contains(entityID)) {
			return true;
		}
		return false;
	}
	
	private static void resolveEvent(Long event) {
		if (!allEvents.contains(event)) {
			return;
		}
		String condObject = ECSManager.getComponentValue(event, "ConditionObject");
		String condComparator = ECSManager.getComponentValue(event, "ConditionComparator");
		String condValue = ECSManager.getComponentValue(event, "ConditionValue");
		if (condObject != null && condComparator != null && condValue != null) {
			if (!checkCondition(condObject, condComparator, condValue)) {
				return;
			}
		}
		Long entity = Long.valueOf(ECSManager.getComponentValue(event, "Entity"));
		String action = ECSManager.getComponentValue(event, "Action");
		String parameters = ECSManager.getComponentValue(event, "Parameters");
		if (entity == null || action == null) {
			return;
		}
		if (ParallelRL.doDebug) {
			String eStr = "event:" + event + "[time:" + ECSManager.getComponentValue(event, "Time");
			eStr += "/action:" + action + "/entity:" + entity;
			eStr += "/parameters:" + parameters;
			eStr += "/cObject:" + condObject;
			eStr += "/cCompare:" + condComparator;
			eStr += "/cValue:" + condValue;
			eStr += "]";
			System.out.println(eStr);
		}
		resolveAction(entity, action, parameters);
	}
	
	private static void resolveAction(Long entity, String action, String parameters) {
		Method actionMethod;
		try {
			actionMethod = Actions.class.getDeclaredMethod(action.toLowerCase(), Long.class, String.class);
			actionMethod.invoke(null, entity, parameters);
		} catch (NoSuchMethodException | SecurityException |
				IllegalAccessException | IllegalArgumentException |
				InvocationTargetException exception) {
			exception.printStackTrace();
		}
		
	}

}
